
#include "SurveillanceCameraManager.h"

#include "SurveillanceControlWidget.h"
#include "Components\SceneCaptureComponent2D.h"
#include "Kismet/KismetMathLibrary.h"
#include "SSurveillanceWindow.h"
#include "Engine\TextureRenderTarget2D.h"
#include "Kismet/KismetMaterialLibrary.h"
#include "Kismet/KismetSystemLibrary.h"


USurveillanceCameraManager::USurveillanceCameraManager()
{
	PrimaryComponentTick.bCanEverTick = true;
	
}

void USurveillanceCameraManager::BeginPlay()
{
	Super::BeginPlay();

	// 初始化
	for (FSurveillanceCamera& Camera:SurveillanceCameras)
	{
	// ------------------------------------- 设置父组件
		{
			// 先判断是不是根组件
			if (GetOwner()->GetRootComponent()->GetName() == Camera.Parent.ToString())
			{
				Camera.ParentCmp = GetOwner()->GetRootComponent();
			}
			else
			{
				// 如果不是根组件，则寻找子组件
				for (auto& FoundCmp:GetOwner()->GetComponents())
				{
					USceneComponent* ParentCmp = Cast<USceneComponent>(FoundCmp);
					if (ParentCmp)
					{
						if (ParentCmp->GetName() != Camera.Parent.ToString())
							continue;

						Camera.ParentCmp = ParentCmp;
					}
				}
			}
		}

	// ------------------------------------- 设置 Transform
		Camera.ActualTransform = Camera.RelativeTransform;
		Camera.GlobalQuat = Camera.RelativeTransform.GetRotation();
	}
}

void USurveillanceCameraManager::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
	
	// 计算位置、旋转
	for (const FSurveillanceCamera& Camera:SurveillanceCameras)
	{
		if (!Camera.ParentCmp)
			continue;
		
		FTransform WorldTransform = Camera.ParentCmp->GetComponentTransform();
		FTransform CameraRelativeTransform = Camera.RelativeTransform;
		
		FTransform CameraTransform;
		CameraTransform.SetLocation(WorldTransform.TransformPosition(Camera.ActualTransform.GetLocation()));
		if (Camera.bGlobal)
		{
			CameraTransform.SetRotation(Camera.GlobalQuat);
		}
		else
		{
			CameraTransform.SetRotation(WorldTransform.TransformRotation(Camera.ActualTransform.GetRotation()));
		}

		if (Camera.SceneCaptureComponent2D.IsValid())
		{
			Camera.SceneCaptureComponent2D->SetWorldLocation(CameraTransform.GetLocation());
			Camera.SceneCaptureComponent2D->SetWorldRotation(CameraTransform.GetRotation());
		}

		if (Camera.bDebug)
		{
			DrawDebugLine(
						GetOwner()->GetWorld(),
						WorldTransform.TransformPosition(CameraRelativeTransform.GetLocation()),
						CameraTransform.GetLocation(),
						FColor::Blue);
		
			DrawDebugCamera(
							GetOwner()->GetWorld(),
					CameraTransform.GetLocation(),
					CameraTransform.GetRotation().Rotator(),
							false,
							Camera.DebugScale,
							Camera.DebugColor);
		}
	}

	// 更新额外相机位置、旋转
	TArray<TWeakObjectPtr<AActor>> AllExtraAvatar;
	ExtraCameraMap.GetKeys(AllExtraAvatar);
	
	for (TWeakObjectPtr<AActor>& ExtraAvatar:AllExtraAvatar)
	{
		if (FSurveillanceCamera* ExtraCameraInfo = ExtraCameraMap.Find(ExtraAvatar))
		{
			if (ExtraCameraInfo->SceneCaptureComponent2D.IsValid())
			{
				ExtraCameraInfo->SceneCaptureComponent2D->SetWorldLocation(ExtraAvatar->GetActorLocation());
				ExtraCameraInfo->SceneCaptureComponent2D->SetWorldRotation(ExtraAvatar->GetActorRotation());
			}
		}
	}

}

void USurveillanceCameraManager::DebugCamera(bool Debug, int32 Index)
{
	if (Index >= SurveillanceCameras.Num())
		return;

	SurveillanceCameras[Index].bDebug = Debug;
}

#pragma region Camera Control
void USurveillanceCameraManager::SetCameraOffset(FVector Offset, int32 CameraIndex)
{
	if (CameraIndex >= SurveillanceCameras.Num())
		return;

	FVector RelativeLocation = SurveillanceCameras[CameraIndex].RelativeTransform.GetLocation();
	FVector ActualTransform = RelativeLocation + Offset;

	SurveillanceCameras[CameraIndex].ActualTransform.SetLocation(ActualTransform);
	//SurveillanceCameras[CameraIndex].SceneCaptureComponent2D->SetWorldTransform(SurveillanceCameras[CameraIndex].ActualTransform);
}

void USurveillanceCameraManager::SetCameraRotation(FRotator Rotator, int32 CameraIndex)
{
	if (CameraIndex >= SurveillanceCameras.Num())
		return;

	float RotationX = Rotator.Roll;
	float RotationY = Rotator.Pitch;
	float RotationZ = Rotator.Yaw;
	FQuat RelativeRotation = SurveillanceCameras[CameraIndex].RelativeTransform.GetRotation();
	
	FVector CameraForward = RelativeRotation.GetForwardVector();
	FVector CameraRight = FVector{0.f, 0.f, 1.f}.Cross(CameraForward).GetSafeNormal();
	
	// Roll
	FQuat QRoll = FRotator{0.f, 0.f, RotationX}.Quaternion();
	// Pitch
	CameraForward = CameraForward.RotateAngleAxis(RotationY, CameraRight);
	// Yaw
	CameraForward = CameraForward.RotateAngleAxis(RotationZ, FVector{0.f, 0.f, 1.f});
	
	SurveillanceCameras[CameraIndex].ActualTransform.SetRotation(CameraForward.ToOrientationQuat() * QRoll);

	// 保存 GlobalQuat
	FTransform WorldTransform = SurveillanceCameras[CameraIndex].ParentCmp->GetComponentTransform();
	SurveillanceCameras[CameraIndex].GlobalQuat = WorldTransform.TransformRotation(CameraForward.ToOrientationQuat() * QRoll);
}

void USurveillanceCameraManager::SetCameraGlobal(bool Global, int32 Index)
{
	if (Index >= SurveillanceCameras.Num())
		return;

	SurveillanceCameras[Index].bGlobal = Global;
}

void USurveillanceCameraManager::SetCameraActive(bool Active, int32 Index)
{
	if (Index >= SurveillanceCameras.Num())
		return;

	if (SurveillanceCameras[Index].SceneCaptureComponent2D.IsValid())
	{
		SurveillanceCameras[Index].SceneCaptureComponent2D->SetActive(Active);
	}
}

void USurveillanceCameraManager::SetCameraResolution(int32 Resolution, int32 Index)
{
	if (Index >= SurveillanceCameras.Num())
		return;

	if (SurveillanceCameras[Index].SceneCaptureComponent2D.IsValid())
	{
		SurveillanceCameras[Index].SceneCaptureComponent2D->TextureTarget->ResizeTarget(Resolution, Resolution);
	}
}

void USurveillanceCameraManager::SetCameraFocalLength(int32 FocalLength, int32 Index)
{
	if (Index >= SurveillanceCameras.Num())
		return;

	if (SurveillanceCameras[Index].SceneCaptureComponent2D.IsValid())
	{
		SurveillanceCameras[Index].SceneCaptureComponent2D->FOVAngle = FMath::RadiansToDegrees(
			2.f * FMath::Atan(SensorWidth / (2.f * FocalLength)));
	}
}

void USurveillanceCameraManager::CreateSurveillanceWnd(int32 Row, int32 Column,
                                                       TSubclassOf<USurveillanceSubWindow> SurveillanceSubWindowClass, float Padding)
{
	// 防止窗口多开
	if (SurveillanceWindow.IsValid())
	{
		// 遍历列表中已添加的的窗口
		for (const auto& ExistingWnd:FSlateApplication::Get().GetTopLevelWindows())
		{
			if (SurveillanceWindow.ToSharedRef() == ExistingWnd)
				return;
		}
		
	}
	CreatedWindowInfo.Row = Row;
	CreatedWindowInfo.Column = Column;
	CreatedWindowInfo.SubWindowType = SurveillanceSubWindowClass;
	CreatedWindowInfo.Padding = Padding;

	// 创建 SceneCaptureComponent2D 和 RenderTarget2D
	CreateSceneCaptureAndRenderTarget();
	// 创建相机窗口初始化参数
	SurveillanceCameraInitArgs InitArgs;
	InitArgs.Row = Row;
	InitArgs.Column = Column;
	InitArgs.Padding = Padding;
	InitArgs.SubWndClass = SurveillanceSubWindowClass;
	InitArgs.World = GetOwner()->GetWorld();
	for (const FSurveillanceCamera& Camera:SurveillanceCameras)
	{
		InitArgs.ImageMaterials.Add(Camera.ViewMaterial);
		InitArgs.CameraNames.Add(Camera.Name);
	}

	TArray<TWeakObjectPtr<AActor>> AllExtraAvatar;
	ExtraCameraMap.GetKeys(AllExtraAvatar);
	for (TWeakObjectPtr<AActor>& ExtraAvatar:AllExtraAvatar)
	{
		if (FSurveillanceCamera* ExtraCameraInfo = ExtraCameraMap.Find(ExtraAvatar))
		{
			InitArgs.ImageMaterials.Add(ExtraCameraInfo->ViewMaterial);
			InitArgs.CameraNames.Add(ExtraCameraInfo->Name);
		}
	}
	
	SurveillanceWindow = SNew(SWindow)
		.Title(FText::FromString(TEXT("相机监控窗口 16:9")))
		.ClientSize(FVector2d{1080.0, 1080.0 / SensorAspectRatio})
		.MinWidth(1080.0)
		.MinHeight(1080.0 / SensorAspectRatio)
		.ShouldPreserveAspectRatio(true)
		.Content()
		[
			SNew(SSurveillanceWindow)
			.CameraInitArgs(InitArgs)
		];

	SurveillanceWindow->SetOnWindowClosed(
		FOnWindowClosed::CreateLambda(
			[this](const TSharedRef<SWindow>& ClosedWind)
			{
				SurveillanceWindow.Reset();
			}
			));
	
	
	FSlateApplication::Get().AddWindow(SurveillanceWindow.ToSharedRef());
	
}

void USurveillanceCameraManager::RefreshSurveillanceWnd()
{
	
	// Debug
	/*GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Silver,
		FString::Printf(TEXT("Wnd is valid: %s"), *StrBool));*/

	if (SurveillanceWindow.IsValid())
	{
		// 1. 销毁窗口
		SurveillanceWindow->RequestDestroyWindow();

		// 2. 延迟到下一帧 重新创建窗口（因为 RequestDestroyWindow 在下一帧才会销毁窗口）
		if (!TH_DelayToNextTick.IsValid())
			TH_DelayToNextTick = UKismetSystemLibrary::K2_SetTimerForNextTick(this, "DelayToCreateWindow");
	}
	else
	{
		// 说明窗口已被关闭，不做任何操作
		return;
	}
}

void USurveillanceCameraManager::DelayToCreateWindow()
{
	if (TH_DelayToNextTick.IsValid())
		UKismetSystemLibrary::K2_ClearAndInvalidateTimerHandle(this, TH_DelayToNextTick);

	SurveillanceWindow.Reset();
	CreateSurveillanceWnd(CreatedWindowInfo.Row, CreatedWindowInfo.Column, CreatedWindowInfo.SubWindowType, CreatedWindowInfo.Padding);
}

void USurveillanceCameraManager::CreateSceneCaptureAndRenderTarget()
{
	for (FSurveillanceCamera& Camera:SurveillanceCameras)
	{
		// 创建 SceneCaptureComponent2D
		if (!Camera.SceneCaptureComponent2D.IsValid())
		{
			Camera.SceneCaptureComponent2D = NewObject<USceneCaptureComponent2D>(this);
			if (Camera.SceneCaptureComponent2D.IsValid())
			{
				Camera.SceneCaptureComponent2D->CaptureSource = ESceneCaptureSource::SCS_FinalToneCurveHDR;
					
				UTextureRenderTarget2D* RenderTarget = NewObject<UTextureRenderTarget2D>(Camera.SceneCaptureComponent2D.Get());
				RenderTarget->InitAutoFormat(512, 512/SensorAspectRatio);// 一定要初始化大小，否则会导致程序崩溃!!!

				Camera.SceneCaptureComponent2D->TextureTarget = RenderTarget;
				if (Camera.SceneCaptureComponent2D->TextureTarget && RenderTargetMaterial)
				{
					if (Camera.ViewMaterial.IsValid())
						Camera.ViewMaterial.Reset();

					if (!Camera.ViewMaterial.IsValid())
					{
						Camera.ViewMaterial = UKismetMaterialLibrary::CreateDynamicMaterialInstance(
						   GetWorld(),
						   RenderTargetMaterial);

						if (Camera.ViewMaterial.IsValid())
						{
							Camera.ViewMaterial->SetTextureParameterValue(FName{"RenderTarget"}, Camera.SceneCaptureComponent2D->TextureTarget);
						}
					}
				}
			
				Camera.SceneCaptureComponent2D->RegisterComponent();
				Camera.SceneCaptureComponent2D->Activate(true);
			}
		}
	}
}

#pragma endregion 

void USurveillanceCameraManager::BindExtraCameraWithActor(AActor* Avatar)
{
	if (!Avatar)
		return;
	
	ExtraCameraMap.Emplace(Avatar, FSurveillanceCamera{});
	if (FSurveillanceCamera* Camerainfo = ExtraCameraMap.Find(Avatar))
	{
		// 创建 SceneCaptureComponent2D
		Camerainfo->SceneCaptureComponent2D = NewObject<USceneCaptureComponent2D>(Avatar);
		if (Camerainfo->SceneCaptureComponent2D.IsValid())
		{
			Camerainfo->SceneCaptureComponent2D->CaptureSource = ESceneCaptureSource::SCS_FinalToneCurveHDR;
			
			UTextureRenderTarget2D* RenderTarget = NewObject<UTextureRenderTarget2D>(Camerainfo->SceneCaptureComponent2D.Get());
			RenderTarget->InitAutoFormat(512, 512/SensorAspectRatio);// 一定要初始化大小，否则会导致程序崩溃!!!

			Camerainfo->SceneCaptureComponent2D->TextureTarget = RenderTarget;
			if (Camerainfo->SceneCaptureComponent2D->TextureTarget && RenderTargetMaterial)
			{
				if (!Camerainfo->ViewMaterial.IsValid())
				{
					Camerainfo->ViewMaterial = UKismetMaterialLibrary::CreateDynamicMaterialInstance(
					   GetWorld(),
					   RenderTargetMaterial);

					if (Camerainfo->ViewMaterial.IsValid())
					{
						Camerainfo->ViewMaterial->SetTextureParameterValue(FName{"RenderTarget"}, Camerainfo->SceneCaptureComponent2D->TextureTarget);
					}
				}
			}
		
			Camerainfo->SceneCaptureComponent2D->RegisterComponent();
			Camerainfo->SceneCaptureComponent2D->Activate(true);
		}
	}
}

void USurveillanceCameraManager::RemoveExtraCameraWithActor(AActor* Avatar)
{
	if (!Avatar)
		return;
	
	if (FSurveillanceCamera* Camerainfo = ExtraCameraMap.Find(Avatar))
	{
		ExtraCameraMap.Remove(Avatar);
		
		RefreshSurveillanceWnd();
	}
}

void USurveillanceCameraManager::SetExtraCameraName(AActor* Avatar, FName Name)
{
	if (Avatar && Name.IsValid())
	{
		if (FSurveillanceCamera* Camerainfo = ExtraCameraMap.Find(Avatar))
		{
			Camerainfo->Name = Name;

			RefreshSurveillanceWnd();
		}
	}
}

void USurveillanceCameraManager::SetExtraCameraResolution(AActor* Avatar, int32 Resolution)
{
	if (FSurveillanceCamera* Camerainfo = ExtraCameraMap.Find(Avatar))
	{
		Camerainfo->SceneCaptureComponent2D->TextureTarget->ResizeTarget(Resolution, Resolution/SensorAspectRatio);
	}
}

void USurveillanceCameraManager::SetExtraCameraFocalLength(AActor* Avatar, float FocalLength)
{
	if (FSurveillanceCamera* Camerainfo = ExtraCameraMap.Find(Avatar))
	{
		Camerainfo->SceneCaptureComponent2D->FOVAngle = FMath::RadiansToDegrees(
			2.f * FMath::Atan(SensorWidth / (2.f * FocalLength)));
	}
}